// Copyright 2007 Google Inc.
// All Rights Reserved.
//
/**
 * @fileoverview BaseMapplet: Create a Google Base query and display results on the map.
 * Will be called from a Mapplet. Alsouses boxover.js
 * @author JTrapp@Google.com (Jens Trapp)
 */

/**
 * Access to the map
 * @type GMap2
 */
var map = new GMap2();
/**
 * Access to the Geocoder
 * @type GClientGeocoder
 */
var geocoder = new GClientGeocoder();

/**
 * cache for markers, reflected as amap, where markers can be retrieved by teh entry id
 * @type 
 */
var markers = new Object();
/**
 * cache for markerInfoTabs, reflected as amap, where markers can be retrieved by teh entry id
 * @type GClientGeocoder
 */
 var markerInfoTabs = new Object();
var entryImages = new Object();
var currentImageIndex = new Object();


/**
 * Show address for the entry. Will be called, when you click on the address to 
 * center the map for the feeds location.
 * @param {String} id Feed entry Id
 */

function showAddress(id) {
    var currentMarker = markers[id];
    if(currentMarker!=null) {
        map.getZoomAsync(function(zoom) {
            if (zoom>13){
                map.panTo(currentMarker);
                _gel("head_div").innerHTML="Moving to "+id+ " (zoom"+zoom+")";
            } else {
                map.setCenter(currentMarker, 13);
                _gel("head_div").innerHTML="jumping to "+id+ " (zoom13)";
            }
        });
        infoTabs = markerInfoTabs[id];
        currentMarker.openInfoWindowTabsHtml(infoTabs);
   } else {
        alert("Null marker for "+id);
   }
}


/**
 * Create a marker on the map for the given address. 
 * @param {String} id Feed entry Id
 * @param {String} name Feed entry title
 * @param {String} address Feed entry location
 * @param {String} attributes HTML description of the feed entry based on the attributs list
 * @param {GIcon} myIcon Icon to show on the map 
 * @param {String} content Entry description 
 */
function createMarkerByLocation(id, name, address, attributes, myIcon, content) {
    var currentMarker=markers[id];
    if (currentMarker==null){
        geocoder.getLocationsAsync(address, function(response) {
            if (!response || response.Status.code != 200) {
                // alert("\"" + address + "\" not found");
            } else {
                place = response.Placemark[0];
                point = new GLatLng(place.Point.coordinates[1],
                        place.Point.coordinates[0]);
                currentMarker =  createMarker(id, point, myIcon);
                showMarker(id, name, currentMarker, address, attributes, myIcon, 
                    content, place.AddressDetails.Accuracy);
             }
         });
    } else{
           showMarker(id, name, currentMarker, address, attributes, myIcon, 
                    content, -3);
    }
}

/**
 * Create a marker on the map for the given point. 
 * @param {String} id Feed entry Id
 * @param {GLatLng} point Point in lat and long
 * @param {GIcon} myIcon Icon to show on the map 
 * @return {GMarker} gmarker
 */
function createMarker(id, point,  myIcon) {
    var marker = new GMarker(point, {icon:myIcon});
    markers[id] = marker;
    GEvent.addListener(marker, "click", function() {
        marker.openInfoWindowTabsHtml( markerInfoTabs[id]);
    });
 
    return marker;
}
    
/**
 * Show a marker on the map. 
 * @param {String} id Feed entry Id
 * @param {String} name Feed entry title
 * @param {GMarker} marker the map marker
 * @param {String} address Feed entry location
 * @param {String} attributes HTML description of the feed entry based on the attributs list
 * @param {GIcon} myIcon Icon to show on the map 
 * @param {String} content Entry description 
 * @param {Number} accuracy the accuracy for the geocoding (0 to 10) 
  */
function showMarker(id, name, marker, address, attributes, myIcon, content, accuracy){
    map.addOverlay(marker);

    var infoTabs = [
        new GInfoWindowTab("Attributes", attributes+address),
        new GInfoWindowTab("Photo", "<div id='tabimage"+id+"'>"+getImageContentTab(id)+"</div>"),
        new GInfoWindowTab("Description", content)
       //, new GInfoWindowTab("Feed")
        ];
    markerInfoTabs[id] = infoTabs;
    var titleEl = _gel ("title"+id);
    if (titleEl!=null){
        titleEl .innerHTML = "<b><a href='javascript:showAddress(\"" 
            +id + "\")'>"+name+"</a> </b>";
    }     
    var locEl = _gel ("loc"+id);
    if (locEl!=null) {
        locEl .innerHTML = "<a href='javascript:showAddress(\"" 
            +id + "\")'>"+address+"</a> ("+accuracy+")";
    } 
}

/**
 * Retrieve the link back to the vendors page. 
 * @param {Element} element element Feed entry
 * @return {String} the link to the entry on the vendors page. 
  */
  function getHREF(element){
    var nodeList = element.getElementsByTagName("link");
    for (var i = 0, node; node = nodeList[i]; i++) {
        var rel = node.getAttribute("rel");
        if ((rel!=null)&&(rel=="alternate")) {
            var href = node.getAttribute("href");
            if (href!=null){
                 return href;
            }
        }
    }
    return "not found";
}

/**
 * Retrieve the first child element for a given tag.
 * This method will only look for direct childs. No grandchilds will be found.
 * For some an unknown reason, I didn't get the standard getElementsByTagNameNS 
 * to work. If you know how, please substitute and let me know.
 * This function should not be use for prefixed nodes. Use getNodeNS instead.
 *
 * @param {Element} element Some Dom element
 * @param {String} tagname The child elements tag name.
 * @return {Node}Child element or the String "[<"+tagname+"> null]" or "[<"+tagname+"> not found]". 
 */
  
  function getNode(element, tagname) {
    var nodeList = element.getElementsByTagName(tagname);
    if (nodeList.length>0) {
        if (nodeList.item(0).firstChild!=null) {
                return nodeList.item(0).firstChild.nodeValue;
        } else {
                return "[<"+tagname+"> null]";
        }
    }
    return "[<"+tagname+"> not found]";
}

/**
 * Retrieve all childs element for a given tag.
 * For some an unknown reason, I didn't get the standard getElementsByTagNameNS 
 * to work. If you know how, please substitute and let me know.
  
 * @param {Element} element Some Dom element
 * @param {String} tagname The child elements tag name, which may include the namespace prefix.
 * @return Child element or the String "[<"+tagname+"> null]". 
 */
 function getNodeNSArray(element, namespacePrefix, tagnames) {
    var tags = tagnames.split(",");
    for (var jj = 0; jj<tags.length; jj++){
        tags[jj]=namespacePrefix+tags[jj];
    }
    var result = new Array();
    var nodeList = element.childNodes;
    for (var counter=0, ii=0, node; node = nodeList[ii]; ii++) {
        for (var jj = 0; jj<tags.length; jj++){
           if (node.nodeName == tags[jj]) {
                if (node.firstChild!=null) {
                   result[counter++] = node.firstChild.nodeValue;
                }
            }
        }
     }
    return result;
}

/**
 * Retrieve the first child element for a given tag.
 * this method will only look for direct childs. No grandchilds will be found.
 * For some an unknown reason, I didn't get the standard getElementsByTagNameNS 
 * to work. If you know how, please substitute and let me know.
  
 * @param {Element} element Some Dom element
 * @param {String} tagname The child elements tag name, which may include the namespace prefix.
 * @return {Node}Child element or the String "[<"+tagname+"> null]" or "[<"+tagname+"> not found]". 
 */
 function getNodeNS(element, tagname) {
     var nodeList = element.childNodes;
    for (var i = 0, node; node = nodeList[i]; i++) {
        if (node.nodeName == tagname){
           if (node.firstChild!=null){
                   return node.firstChild.nodeValue;
            } else {
                    //alert("Empty node: "+node.nodeName);
            }
        }
     }
    return null;
}

/**
 * Retrieve the first descendant element for a given tag.
 * This method will look for the code in all descendants of the element
 * For some an unknown reason, I didn't get the standard getElementsByTagNameNS 
 * to work. If you know how, please substitute and let me know.
 * 
 * @param {Element} element Some Dom element
 * @param {String} tagname The child elements tag name, which may include the namespace prefix.
 * @return {Node}Child element or the String "[<"+tagname+"> null]" or "[<"+tagname+"> not found]". 
 */
 function getNodeNSRecursive(element, tagname){
    var nodeList = element.childNodes;
    for (var i = 0, node; node = nodeList[i]; i++) {
        if (node.nodeName == tagname) {
           if (node.firstChild!=null){
                   return node.firstChild.nodeValue;
            } else {
                //    alert("Empty node: "+node.nodeName);
            }
        } else {
            var result = getNodeNSRecursive(node, tagname);
            if (result!=null){
                return result;
            }
            
        }
     }
    return null;
}


/**
 * Create a HTML table for the entries attributes. Which attributes are shown, 
 * can be set using the User preferences. 
 * @param {_IG_Prefs} prefs The user preferences and access to localisations
 * @param {String} id Feed entry Id
 * @param {String} link A link backt to the vendors page of the entry
 * @param {String} name the name for the entry
 * @param {Element} elements the Dom Tree of the entry
 * @return {String} an html table used as snipplet. 
 */
function getAttributesTable(prefs, id, link, name, elements)
{
   var attributesHTML = 
        "<table width ='100%'><tr><td colspan='3' colName='title' id='title"
        +id+"'><b>"+name+"</b></td>";
//        +"<td valign='top' align='right'><input type='button' onclick="
//        +"'showAddress(\""+location+"\")' value='C'/></td></tr>";
//             

    var attributes = prefs.getArray("attributes");
    for (var i = 0, attribute; attribute = attributes[i]; i++) {
        var attributeTag = "g:"+prefs.getMsg("tag_"+attribute);
        //var value = elements.getElementsByTagNameNS("http://base.google.com/ns/1.0", prefs.getMsg("tag_"+attribute)).nodeValue;

        var value = getNodeNS(elements, attributeTag);
        if (value!=null)
        {
            attributesHTML += "<tr><td colName='key' align='right'>"
                    +prefs.getMsg(attribute)
                    +":</td><td colName='value' colspan='2'>"
                    +value+"</td></tr>";
        }
    }
    attributesHTML+="<tr><td colspan='1' colName='feedlink'>"
    if (prefs.getBool("debug")){
        attributesHTML+="<font size='-2'><a href='"+id+"'>Feed Entry</a></font>"
    }
    attributesHTML += "</td><td colspan='2' colName='feedlink'>";
    if (link!=null) {
           attributesHTML += "<font size='-2'><a target ='basealternate' href='"
            +link+"'>Goto Website</a></font>";
    }
    attributesHTML +="</td></tr></table>";
    return attributesHTML;
}


/**
 * Create an icon using the default settings used in the prefs. 
 * To change the default icon or size, open the massages file, which is used to 
 * localize the mapplet. Look for the entries:
 * <dl>
 *    <dt>defaultIcon</dt><dd>The URL of the icon image</dd>
 *    <dt>defaultIconWidth</dt><dd>The icon width used on the map</dd>
 *    <dt>defaultIconHeight</dt><dd>The icon height used on for the map icon.</dd>
 *    <dt>defaultIconAnchorX</dt><dd>The image coordinate, that will be used to place the marker icon</dd>
 *    <dt>defaultIconAnchorY</dt><dd>The image coordinate, that will be used to place the marker icon</dd>
 *    <dt>defaultIconWindowX</dt><dd>The coordinate at which the window will popup</dd>
 *    <dt>defaultIconWindowY</dt><dd>The coordinate at which the window will popup</dd>   
 * </dl>
 * The image url can be overwritten using the user preferences to customize the demo.
 *
 * @param {_IG_Prefs} prefs The user preferences and access to localisations
 * @return {GIcon} the icon
 */
 function getDefaultIcon(prefs){
    var icon = new GIcon();
    icon.iconSize = new GSize(
        prefs.getMsg("defaultIconWidth"),
        prefs.getMsg("defaultIconHeight"));
    icon.iconAnchor = new GPoint(
        prefs.getMsg("defaultIconAnchorX"),
        prefs.getMsg("defaultIconAnchorY"));
    icon.infoWindowAnchor = new GPoint(
        prefs.getMsg("defaultIconWindowX"),
        prefs.getMsg("defaultIconWindowY"));

    if(prefs.getString("icon")!=null && (prefs.getString("icon").length!=0)) {
        icon.image = prefs.getString("icon");
    }else {
        icon.image = prefs.getMsg("defaultIcon");       
    }
    return icon; 
}

/**
* Retrieve the maximum number of results that shall be retrieved at each query.
*  The number can be specified using the
* user preferences. To prevent spamming, the maximum is limited to 250 entries.
* @param {_IG_Prefs} prefs The user preferences and access to localisations
*@return{Number} the maximum number of entries to retrieve per query.
*/
function getMaxResults(prefs){
    var maxResults=prefs.getInt("maxResults");
    if ((maxResults<=0) || (maxResults>250)){
            maxResults =10;
            prefs.set("maxResults", maxResults);
    }
    return maxResults;
}
    

/**
* Create a HTML snipplet for retrieving more results similar to the google search results.
* @param {_IG_Prefs} prefs The user preferences and access to localisations
* @param {Element} form The form DOM Object
* @param [int} currentIndex start index of the current search
* @param {Number} numResults number of total results
* @return {String} A HTML navigation snipplet
*/
function getMore(prefs, form, currentIndex, numResults){
    if ((numResults == null) || (numResults==0)){
        return "</table><tr><td>No Results found.</td></tr></table>";
    }
    var maxResults=getMaxResults(prefs);
    var max = numResults/maxResults;
    if (max>10) max = 10;
    var result = "<table border=0 cellpadding=0 width=1% cellspacing=0 align=center>"
        +"<tr>";
    for (var ii = 1; ii<max; ii++){
        if  ((currentIndex-1)/maxResults == ii-1){
            result+="<td nowrap><span class=i>"+ii+"</span></td>";
        }
        else{
            var index = (ii-1)*maxResults+1;
            result+="<td nowrap><a href='javascript:evaluateQuery(prefs, form, "+index
                + ")'><div class=nr></div> "+ii+" </a></td>";
        }
      }
      var nextIndex = currentIndex+maxResults;
      result += "<td nowrap class=b><a href='javascript:evaluateQuery(prefs, form, "
            +nextIndex+")'>"+prefs.getMsg("searchMore")+"</a></table>"; 

      return result;
}

/**
* Build the result page for the Google Base query. The function will iterate 
* over all entries in the response, create result list in html form and place
* a marker on the map. 
*
* @param {_IG_Prefs} prefs The user preferences and access to localisations
* @param {Elements} response The base response as DOM
* @return the result HTML table
*/
function appendResults(prefs, response) {
    var itemList = response.getElementsByTagName("entry");
    var tinyIcon = getDefaultIcon(prefs);
    var html = "<table>";
            // Loop through all <food> nodes
    for (var i = 0, el; el = itemList[i]; i++) {
        var name = getNode(el, "title");
        var id = getNode(el, "id");
        var content = getNode(el, "content");
        var description = getNode(el, "description");
        var href = getHREF(el);

        entryImages[id] = getNodeNSArray(el, "g:", prefs.getMsg("tags_image_link"));
        if (entryImages[id].length==0){
            entryImages[id][0]=tinyIcon.image;
        }
        currentImageIndex[id]=-1;
        var location=getNodeNS(el, "g:"+prefs.getMsg("tag_location"));   

        //html +="<tr><td><a href='"+link+"'>"+link+"</a></td></tr>";
        html += "<tr><td id='image"+id+"' width='68'>"
            +getImageContent(id, "rollImages")
            + "</td><td valign='top' "+
            "TITLE='header=[Attributes] body=["+content+"]' " + ">";
        var imageIcon = tinyIcon;
        if (prefs.getBool("showImageAsIcon")){
                imageIcon = getDefaultIcon(prefs);
                imageIcon.image=entryImages[id][0];
        }
        var attributesHTML = getAttributesTable(prefs, id, href, name, el);
        var point = createMarkerByLocation(id, name, location, attributesHTML,
            imageIcon, content);

         html += attributesHTML+"</td></tr><tr><td id='loc"+id+"' "
            +"colspan='6' colName='location'>";

        if (markers[id]!=null) {
            html += "<a href='javascript:showAddress(\"" +id + "\")'>"+location+"</a>";
        } else {
            html += location;
        }
       html+= "</td></tr>"; 
    }
        
    //html += "<input type='button' onclick="showAdress(location, content)" 
    // value="Show in Map" />"; 

    return html+"</table>";
}

/**
* Event handler for rolling the images.
* If more than one image is provided, the programm will iterate over the images
* every time you  click on the image.
 * @param {String} id Feed entry Id
 */
function rollImages(id)
{
    if (_gel("image"+id)!=null){
        _gel("image"+id).innerHTML = getImageContent(id, "rollImages");
    }
}

/** 
* Create a HTML Snipplet to show the enries image. If more than one image is 
* available, the HTML created will create a clickable image which will than be 
* used to iterate over the images.
* @param {String} id Feed entry Id
* @return {String} an HTML snipplet for showing an image. 
*/
function getImageContent(id, eventHandler){
    var result ;
    var images = entryImages[id];
    if (images.length==1){
        result = "<img width='64' src='"+entryImages[id][0]+"'/>";
    } else {
       currentImageIndex[id]=(currentImageIndex[id]+1)%images.length;

    //        if (markers[id]!=null)
    //        {
    //            result = "<a href='javascript:showAddressPic(\"" +id 
    //                + "\")'><img width='64' src='"+entryImages[id][images]
    //                +"' onmouseover=\"rollImages('"+id+"')\"></a>";
    //        }
    //        else
        {
            result = "<a href=\"javascript:"+eventHandler+"('"+id
            +"')\"><img width='64' src='"+images[currentImageIndex[id]]
            +"' onmouseover=\""+eventHandler+"('"+id+"')/></a>";
        }

    }
    return result;

}

/** 
* Create a tab for the little map window, showing the images of the entry.
* Due to restictions no javascript is allowed here, so we can only show some 
* static stuff. No rolling images here.
* @param {String} id Feed entry Id
*/
function getImageContentTab(id){
    var result ="<table>";
    var images = entryImages[id];
    var max= images.length;
    if (max>3){
         max=3;
    }
    var size = 180/max;
    for (var ii=0; ii<max;ii++){
        result+= "<td><a href='"+images[ii]+"'><img width='"+size+"' src='"+images[ii]+"'/></a></td>";
    }
    return result+"<table>";

}
      
/**
* Event Handler for evaluating queries 
*
* @param {_IG_Prefs} prefs The user preferences and access to localisations
* @param {Element} form Access to the query parameters of the html form
* @param {Number} start
*/
function evaluateQuery(prefs, form, startIndex){
    map.clearOverlays();
    map.getCenterAsync(function(center){
       var maxResults=getMaxResults(prefs);
    
       var url = prefs.getMsg("baseURL");
       var fullText = _trim(form.textSearchInput.value);
       var queryString=prefs.getMsg("item-type")+"?bq="+_esc(fullText);

       queryString += _esc(buildQueryString(prefs, form));
       queryString += _esc("["+prefs.getMsg("tag_location")+"(location)]")+"&orderby="
            +_esc("[x = "+prefs.getMsg("tag_location")+"(location): neg(min(dist(x, @\""
            +center.toUrlValue(6) +"\")))]")+"&max-results="+maxResults
            +"&start-index="+startIndex;

       _gel("content_div").innerHTML = "<i>"+prefs.getMsg("searching")+"</i><br/>"
                +"URL: "+url+queryString+"<br/>";

       _IG_FetchXmlContent(url+queryString, function (response) {
            var message ="";
            if (response == null || typeof(response) != "object" ||
                response.firstChild == null){
                message = "<i>Invalid data.</i><br/>"
                    +"URL: <a href='"+url+queryString+"'>"+url+queryString+"</a><br/>"
                    +"Response Message: "+response;
            }
            else
            {
        //var numResults = response.getElementsByTagNameNS("http://a9.com/-/spec/opensearchrss/1.0/","totalResults");
        var numResults = getNodeNSRecursive(response, "openSearch:totalResults");
          
        _gel("resultSummary").innerHTML ="<font size='-2'>Found "+numResults+" items.";
        
        var moreText = getMore(prefs, form, startIndex, numResults);
        message = appendResults(prefs, response);
        if (prefs.getBool("debug")) {
            message += "URL: <a href='"+url+queryString+"'>"+url+queryString+"</a><br/>";
        }

            
        }
            _gel("content_div").innerHTML = message+moreText
            +"<br/>Powered by<br/><img src='http://base.google.com/base/search/intl/en_us/images/googleBase.gif'/>";
            _IG_AdjustIFrameHeight();
       });
   });
   
    _gel("refine_div").style.display = "";
    _gel("form_div").style.display="none";
    _IG_AdjustIFrameHeight();
}

/**
* Create a HTML input field for colors and place it in the form.
* car specific stuff
*/   
function initInputSelections(prefs, form, element, any, selections){
    var result = "<select name='"+selections+"'>";
    result+="<option value='any'>"+prefs.getMsg(any)+"</option>";   
    var selectionsString = prefs.getMsg(selections);
    if (selectionsString!=null) {
        var selections = selectionsString.split(",");
        for (var ii=0; ii<selections.length;ii++){
             result+="<option>"+selections[ii]+"</option>";   
        }
    }
    _gel(element).innerHTML= result+"</select>";
}


/** 
* Show the search form
*/
function showSearchForm(){
    _gel("refine_div").style.display = "none";
    _gel("form_div").style.display="";
    _IG_AdjustIFrameHeight();
    
 }



